<template>
  <div class="chart-inst"></div>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
import * as d3 from "d3";
import Icon from "../../TriangleChart/Icon";

const POINT_RADIUS = 3;

@Component({
  name: "ChartCount",
  components: {}
})
export default class ChartCount extends Vue {
  @Prop()
  private option!: ComplexCountColumn;

  @Prop({
    default: false
  })
  private grid!: boolean;

  @Prop()
  private range!: [number, number];

  @Prop({
    required: true
  })
  private ticks!: ComplexTickCconfig;

  private svg!: d3.Selection<any, any, any, any>;

  private h!: number;
  private w!: number;

  private scale!: d3.ScaleLinear<any, any, any>;

  @Watch("option", { immediate: true })
  onOptionEntry() {
    this.$nextTick(() => {
      this.start();
    });
  }

  start() {
    this.$el.innerHTML = "";

    const rect = this.$el.getBoundingClientRect();
    this.h = rect.height;
    this.w = rect.width;

    this.scale = d3.scaleLinear().range([0, this.h]).domain(this.range).nice();

    this.svg = d3
      .select(this.$el)
      .append("svg")
      .attr("width", this.w)
      .attr("height", this.h);

    this.addGrids();

    for (let i = 0; i < this.option.children.length; i++) {
      const config = this.option.children[i];
      if (config.type === "line") {
        this.drawCountLine(config);
      } else if (config.type === "area") {
        this.drawCountArea(config);
      } else if (config.type === "bar") {
        this.drawCountBar(config);
      } else {
        this.drawCountPoint(config);
      }
    }

    this.bindEvent();
  }

  addGrids() {
    if (this.ticks.visible !== true) return;

    const step = this.ticks.formatStep || 200;

    const tickSet: Array<number> = [];
    const axis = d3
      .axisRight(this.scale)
      .ticks(this.ticks.total)
      .tickFormat((d: any) => {
        tickSet.push(d);
        return d % step === 0 ? d : "";
      });

    const ag = this.svg.append("g").classed("sp-axis", true);
    ag.call(axis);

    const grid = this.svg.append("g").classed("sp-grid", true);

    const deep = "rgba(0, 0, 0, 0.4)",
      normal = "rgba(0, 0, 0, 0.1)";

    grid
      .append("g")
      .classed("ylines", true)
      .selectAll("line")
      .data(tickSet)
      .enter()
      .append("line")
      .attr("x1", 0)
      .attr("y1", (d) => this.scale(d))
      .attr("x2", this.w)
      .attr("y2", (d) => this.scale(d))
      .style("stroke", (d: any) => {
        return d % step === 0 ? deep : normal;
      })
      .style("stroke-width", 1);

    const xlines = grid.append("g").classed("xlines", true);
    const xStep = this.w / 10;
    for (let i = 0; i < 10; i++) {
      xlines
        .append("line")
        .attr("x1", i * xStep)
        .attr("y1", 0)
        .attr("x2", i * xStep)
        .attr("y2", this.h)
        .style("stroke", normal)
        .style("stroke-width", 1);
    }
  }

  bindEvent() {
    const _this = this;
    d3.selectAll(".data-point")
      .on("mousemove", function (this: any) {
        const node = d3.select(this);
        const data = node.datum() as any;
        const sery = node.attr("data-sery");
        const rect = this.getBoundingClientRect();
        _this.$emit("tooltip", {
          name: sery,
          depth: data.depth,
          value: data.value,
          x: rect.left,
          y: rect.top
        });
      })
      .on("mouseout", function () {
        _this.$emit("close");
      });
  }

  drawCountLine(config: ComplexLineCountColumn) {
    const xScale = d3.scaleLinear().range([0, this.w]).domain(config.xRange);
    const g = this.svg.append("g");
    const color = config.color || "#000";

    const line = d3
      .line<{ depth: number; value: number }>()
      .curve(d3.curveCardinal)
      .x((d) => xScale(d.value))
      .y((d) => this.scale(d.depth));

    g.append("path")
      .attr("d", line(config.data))
      .style("fill", "none")
      .style("stroke", color);

    config.data.forEach((d) => {
      const x = xScale(d.value);
      const y = this.scale(d.depth);
      g.append("circle")
        .attr("cx", x)
        .attr("cy", y)
        .attr("r", POINT_RADIUS)
        .style("fill", color);

      g.append("circle")
        .classed("data-point", true)
        .attr("data-sery", config.headerLabel)
        .attr("cx", x)
        .attr("cy", y)
        .datum(d)
        .attr("r", POINT_RADIUS * 3)
        .style("fill", "transparent");
    });
  }

  drawCountArea(config: ComplexAreaCountColumn) {
    const xScale = d3.scaleLinear().range([0, this.w]).domain(config.xRange);
    const g = this.svg.append("g");
    const path = d3.path();
    const color = config.color || "#000";
    const points: Array<{ x: number; y: number }> = [];
    config.data.forEach((d, i) => {
      const x = xScale(d.value);
      const y = this.scale(d.depth);

      if (i === 0) {
        path.moveTo(0, y);
        path.lineTo(x, y);
      } else if (i === config.data.length - 1) {
        path.lineTo(x, y);
        path.lineTo(0, y);
      } else {
        path.lineTo(x, y);
      }
    });
    // path.closePath();
    g.append("path")
      .attr("d", path.toString())
      .style("fill", color)
      .style("stroke-width", "0");
    const border = g.append("g").classed("border", true);
    points.forEach((d, i) => {
      if (i === 0) {
        path.moveTo(d.x, d.y);
      } else {
        path.lineTo(d.x, d.y);
      }
    });

    border
      .append("path")
      .attr("d", path.toString())
      .style("stroke", "#000")
      .style("stroke-width", 2)
      .style("fill", "none");

    const main = g
      .append("g")
      .classed("area-main", true)
      .selectAll("g")
      .data(config.data)
      .enter()
      .append("g");

    main
      .append("path")
      .attr("d", (d) => {
        const x = xScale(d.value);
        const y = this.scale(d.depth);
        return `M${x},${y}L0,${y}`;
      })
      .attr("stroke", "#000")
      .attr("stroke-width", 2);

    // main
    //   .append("g")
    //   .classed("rpoint", true)
    //   .selectAll("circle")
    //   .data(config.data)
    //   .enter()
    //   .append("circle")
    //   .attr("r", POINT_RADIUS)
    //   .attr("cx", (d) => xScale(d.value))
    //   .attr("cy", (d) => this.scale(d.depth))
    //   .attr("fill", "#000");

    main
      .append("g")
      .classed("rpoint", true)
      .selectAll("circle")
      .data(config.data)
      .enter()
      .append("circle")
      .attr("r", POINT_RADIUS * 3)
      .classed("data-point", true)
      .attr("data-sery", config.headerLabel)
      .attr("cx", (d) => xScale(d.value))
      .attr("cy", (d) => this.scale(d.depth))
      .attr("fill", "transparent");
  }

  drawCountBar(config: ComplexBarCountColumn) {
    const xScale = d3.scaleLinear().range([0, this.w]).domain(config.xRange);
    const color = config.color || "#000";
    const w = config.barWidth || 3;

    this.svg
      .selectAll("rect")
      .data(config.data)
      .enter()
      .append("rect")
      .classed("data-point", true)
      .attr("data-sery", config.headerLabel)
      .attr("x", 0)
      .attr("y", (d) => this.scale(d.depth) - w / 2)
      .attr("width", (d) => xScale(d.value))
      .attr("height", w)
      .style("fill", color);
  }

  drawCountPoint(config: ComplexPointCountColumn) {
    const defs = this.svg.append("defs");
    const xScale = d3.scaleLinear().range([0, this.w]).domain(config.xRange);

    const id = Icon.createIconMapping(config.icon, defs);

    const size = config.icon.size || 10;

    this.svg
      .selectAll("use")
      .data(config.data)
      .enter()
      .append("use")
      .classed("data-point", true)
      .attr("data-sery", config.headerLabel)
      .attr("width", size)
      .attr("height", size)
      .attr("x", (d) => xScale(d.value) - size / 2)
      .attr("y", (d) => this.scale(d.depth) - size / 2)
      .attr("xlink:href", "#" + id);
  }
}
</script>
<style lang="scss" scoped>
.chart-inst {
  width: 100%;
  height: 100%;
  ::v-deep {
    text {
      font-size: 14px;
    }
    .data-point {
      cursor: pointer;
    }
    .sp-axis {
      visibility: hidden;
    }
  }
}
</style>
